﻿module WingBeats.XHTML.Query2

open WingBeats.XHTML.CSS.Selector

/// This function definitely needs to be rewritten, if it is even needed at all.
let QueryBySelector (str : string) elements =
    let selectors = Language.Parse str                       
    
    let rec queryBySelectors selectors elements =
    
        let rec matchBySelectorParts parts element =
            let matchByType tag element = 
                match element with
                | Element (tag2, _, _) -> tag2.Equals(tag)
                | Inner   _            -> false
            let matchByID identifier element =
                match element with
                | Element(_, attribs, _) -> 
                    let elementId = 
                        attribs
                        |> List.tryPick(fun attrib -> match attrib with
                                                      | A.Id _ -> Some attrib
                                                      | _      -> None)
                    match elementId with
                    | Some(A.Id ident) -> ident.Equals(identifier)
                    | _                -> false
                | Inner _ -> false 
            let matchByClass identifier element =
                match element with
                | Element (_, attribs, _) ->
                    let classes =
                        attribs
                        |> List.tryPick(fun attrib -> match attrib with
                                                      | A.Class _ -> Some attrib
                                                      | _         -> None)
                    match classes with
                    | Some(A.Class strings) -> strings |> Seq.exists(fun s -> s.Equals(identifier))
                    | _                     -> false
                | Inner _ -> false
            match parts with
            | Ast.Universal                :: tail -> failwith "Universal selector not implemented yet."           
            | Ast.Type          tag        :: tail -> if matchByType tag element 
                                                        then matchBySelectorParts tail element
                                                        else []
            | Ast.ID            identifier :: tail -> if matchByID identifier element 
                                                        then matchBySelectorParts tail element
                                                        else []
            | Ast.Class         identifier :: tail -> if matchByClass identifier element
                                                        then matchBySelectorParts tail element
                                                        else []
            | Ast.Attribute     _          :: tail
            | Ast.Pseudo        _          :: tail
            | Ast.PseudoElement _          :: tail -> failwith "This type of simple selector part is not implemented yet."
            | []                                   -> [element]
        
        let combinate combinator sections matches element =
            
            let rec orphanize matches =
                match matches with
                | Element (_, _, children) :: tail -> children @ orphanize tail
                | []                               -> []
                | Inner _ :: tail                  -> []       @ orphanize tail
            
            match combinator with
            | Ast.Descendant           -> queryBySelectors [ Ast.Selector sections ] (orphanize matches)
            | Ast.Child                -> failwith "Child combinator not implemented"
            | Ast.ImmediatelyProceeded
            | Ast.Preceeded            -> failwith "Combinators immediately preceeded by and preceeded by not implemented."
        
        let rec matchBySelectorSections sections matches element =
            match sections with
            | Ast.SimpleSelector selectorParts :: tail -> let mtch = matchBySelectorParts selectorParts element 
                                                          matchBySelectorSections tail (matches @ mtch) element
            | Ast.Combinator     combinator    :: tail -> combinate combinator tail matches element
            | Ast.Selector       _             :: tail -> failwith "A selector must not be part of another selector"
            | []                                       -> matches

        let rec matchBySelector selectors element =
            match selectors with
            | selector :: tail -> 
                match selector with
                | Ast.Selector selectorSections -> matchBySelectorSections selectorSections [] element @ matchBySelector tail element                                     
                | Ast.SimpleSelector _ 
                | Ast.Combinator     _ -> failwith "Simple selectors and combinators must be part of a selector."         
            | [] -> []
        
        let rec forEachElement elements =
            match elements with
            | element :: tail ->
                match element with
                | Element (tag, attributes, elements) -> matchBySelector selectors element @ forEachElement elements @ forEachElement tail
                | Inner   _                           -> [] @ forEachElement tail
            | [] -> []
        
        forEachElement elements
    
    queryBySelectors selectors elements
